<?php
/**
 * @author Jumper Swordwave
 * date: 2021/04/20
 */
declare(strict_types=1);
namespace App\Game\Task;

use Hyperf\Task\Annotation\Task;
use Hyperf\Server\ServerFactory;
use Hyperf\Utils\Collection;
use Hyperf\Redis\Redis;
use App\Constants\ErrorCode;
use App\Game\Service\{RoomService, UserService};
use App\Game\Model\{Room, User};
use App\Game\Core\{Response, Packet, Command};
use App\Log;

/**
 * game task
 */
class GameTask
{
    /**
     * @var string
     */
    private $room_lock_key;

    public function __construct()
    {
        $this->room_lock_key = config('game.key.room_lock', 'room:search:lock');
    }

    /**
     * 匹配房间
     */
    public function matchRoom(User $user)
    {
        Log::info('game task match');
        $container = container();
        $redis = $container->get(Redis::class);
        $roomService = $container->get(RoomService::class);
        $userService = $container->get(UserService::class);
        while(1) {
            if ($redis->setnx($this->room_lock_key, true)) {
                $redis->expire($this->room_lock_key, config('game.key.expire', 10));
                $srv = server();

                $room = $roomService->searchRoom();
                if ($room == null || !$roomService->userJoin($room, $user)) {
                    $res = Response::error(ErrorCode::ROOM_ERROR, '没有空闲的座位');
                    $srv->push($user->id, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
                    $redis->del($this->room_lock_key);
                    Log::info('没有空位', $room->toArray());
                    break;
                }
                $users = $userService->findUsers($room->users);
                $userService->ready($user); //玩家进入房间,处于准备状态

                //发送用户加入消息
                foreach($users as $_user) {
                    $res = Response::success(Command::GAME_USER_JOIN_RES, [
                        'user' => $_user->toArray(),
                        'room' => $room->info(),
                        'users' => $users->map(function($u){
                            return [
                                'id' => $u->id,
                                'position' => $u->position,
                                'nickname' => $u->nickname,
                            ];
                        })->toArray(),
                    ]);
                    $srv->push($_user->fd, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
                }
                
                $redis->del($this->room_lock_key);
                $roomService->saveRoom($room);
                Log::info('user join ', $room->toArray());
                //房间内所有玩家处于准备状态,游戏开始
                if ($roomService->allReady($room)) {
                    $this->gameStart($room);
                }
                break;
            }
            Log::info('等待进入搜索队列');
            usleep(500);
        }
    }

    /**
     * 玩家准备就绪
     */
    public function userReady(Room $room)
    {
        Log::info('user ready', $room->toArray());
        $container = container();
        $srv = server();
        $roomService = container()->get(RoomService::class);

        if ($roomService->allReady($room)) {    //玩家都已准备就绪,游戏开始
            return $this->gameStart($room);
        }
        
        //通知有玩家准备就绪
        $userService = container()->get(UserService::class);
        $users = $userService->findUsers($room->users);
        $send_users = $users->map(function($u){
            return [
                'id' => $u->id,
                'position' => $u->positionk,
                'nickname' => $u->nickname,
            ];
        })->toArray();

        foreach($users as $user) {
            $res = Response::success(Command::GAME_USER_READY_RES, [
                'user' => $user->toArray(),
                'room' => $room->info(),
                'users' => $send_users,
            ]);
            $srv->push($user->fd, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
        }
    }

    /**
     * 游戏开始
     */
    public function gameStart(Room $room)
    {
        Log::info('game start', $room->toArray());
        $roomService = container()->get(RoomService::class);
        $userService = container()->get(UserService::class);

        //游戏开始
        $room = $roomService->gameStart($room);

        //发牌完成,广播叫地主
        return $this->gameCall($room);
    }
    
    /**
     * 通知玩家开始叫地主
     */
    public function gameCall(Room $room)
    {
        Log::info('start game call', $room->toArray());
        $srv = server();
        $userService = container()->get(UserService::class);
        $users = $userService->findUsers($room->users);
        $send_users = $this->formatSendUsers($users, $room);

        $master_user = $userService->findUserById($room->getUserId($room->master));
        foreach($users as $user) {
            $command = Command::GAME_CALL_RES;
            $res = Response::success($command, [
                'user' => $user->toArray(),     //个人信息
                'room' => $room->info(),     //房间信息
                'users' => $send_users,         //玩家信息
                'hands' => $room->getHands($user->position),    //我的手牌
            ]);
            $srv->push($user->fd, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
        }
    }

    /**
     * 通知玩家底牌消息
     */
    public function baseCard(Room $room)
    {
        Log::info('base card send', $room->toArray());
        $srv = server();
        $userService = container()->get(UserService::class);
        $users = $userService->findUsers($room->users);
        $send_users = $this->formatSendUsers($users, $room);

        foreach($users as $user) {
            $res = Response::success(Command::GAME_BASECARD_RES, [
                'user' => $user->toArray(), //个人信息
                'room' => $room->info(),
                'hands' => $room->getHands($user->position),    //我的手牌
                'users' => $send_users,     //用户信息
            ]);
            $srv->push($user->fd, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
        }
    }

    /**
     * 通知玩家出牌
     */
    public function outCard(Room $room)
    {
        Log::info('user out cards', $room->toArray());
        $srv = server();
        $userService = container()->get(UserService::class);
        $users = $userService->findUsers($room->users);
        $send_users = $this->formatSendUsers($users, $room);

        foreach($users as $user) {
            $res = Response::success(Command::GAME_OUT_RES, [
                'user' => $user->toArray(),
                'room' => $room->info(),
                'hands' => $room->getHands($user->position),    //我的手牌
                'users' => $send_users,
            ]);
            $srv->push($user->fd, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
        }
    }
    
    /**
     * 游戏结束,通知玩家结算信息
     */
    public function gameover(Room $room)
    {
        Log::info('game over', $room->toArray());
        $srv = server();
        $userService = container()->get(UserService::class);
        $roomService = container()->get(RoomService::class);
        $users = $userService->findUsers($room->users);
        $send_users = $this->formatSendUsers($users, $room);

        $roomService->gameOver($room);
        $wintype = $room->winType();

        foreach($users as $user) {
            $res = Response::success(Command::GAME_OVER_RES, [
                'user' => $user->toArray(),
                'room' => $room->info(),
                'wintype' => $wintype,
                'users' => $send_users,
            ]);
            $srv->push($user->fd, Packet::encode($res->getResult()), WEBSOCKET_OPCODE_BINARY);
        }
    }
    
    private function formatSendUsers(Collection $users, Room $room) 
    {
        return $users->map(function($u) use($room){
            return [
                'id' => $u->id,
                'position' => $u->position,     //玩家位置
                'nickname' => $u->nickname,
                'hand_count' => count($room->getHands($u->position)),   //手牌数量
                'current' => $u->id == $room->getCurrentUid(),      //是否当前动作人员
                'master' => $u->id == $room->getMasterUid(),  //是否地主
            ];
        })->toArray();
    }
}